package com.donger.mes.system.mes.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.donger.common.core.utils.BizException;
import com.donger.common.sso.utils.AuthUtils;
import com.donger.mes.system.mes.dto.DevoteMaterail;
import com.donger.mes.system.mes.dto.WorkCenterMatPlatformDTO;
import com.donger.mes.system.mes.dto.WorkDTO;
import com.donger.mes.system.mes.dto.WorkPlatformDTO;
import com.donger.mes.system.mes.entity.DevoteMaterailLog;
import com.donger.mes.system.mes.entity.WorkCenterMat;
import com.donger.mes.system.mes.entity.WorkCenterPlan;
import com.donger.mes.system.mes.entity.WorkReportLog;
import com.donger.mes.system.mes.enums.PlanStatusEnum;
import com.donger.mes.system.mes.event.WorkCenterWorkEvent;
import com.donger.mes.system.mes.mapper.WorkCenterPlanMapper;
import com.donger.mes.system.mes.service.DevoteMaterailLogService;
import com.donger.mes.system.mes.service.WorkCenterMatService;
import com.donger.mes.system.mes.service.WorkCenterPlanService;
import com.donger.mes.system.mes.service.WorkReportLogService;
import com.donger.mes.system.mes.work.WorkHandle;
import com.donger.mes.system.side.service.MesSideWarehouseService;
import com.donger.mes.utils.mes.MatDetail;
import com.donger.mes.utils.mes.MesUtils;
import lombok.AllArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;


/**
 * 工作中心业务逻辑处理
 */
@Service
@AllArgsConstructor
public class WorkCenterPlanServiceImpl extends ServiceImpl<WorkCenterPlanMapper, WorkCenterPlan> implements WorkCenterPlanService {

    private final WorkCenterMatService workCenterMatService;

    private final MesSideWarehouseService mesSideWarehouseService;

    private final Map<String, WorkHandle> workHandleMap;

    private final ApplicationEventPublisher applicationEventPublisher;


    private final DevoteMaterailLogService devoteMaterailLogService;

    private final WorkReportLogService workReportLogService;


    /**
     * 工作中心计划启动
     * 查询工作中心状态
     * 查询是否上岗
     * 查询对应数据是否正确
     * 查询当前工作中心是否执行其他计划
     * <p>
     * <p>
     * 启动工作中心
     * <p>
     * 启动对应的设备
     *
     * @param workCenterPlanCode
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void release(String workCenterPlanCode) {

        WorkCenterPlan workCenterPlan = this.getByWorkCenterPlanCode(workCenterPlanCode);

        // 校验状态信息
        if (!workCenterPlan.getStatusCode().equals(PlanStatusEnum.WAITING.getType())) {
            throw new BizException("工作中心状态不是等待中，不能启动");
        }


        // TODO 校验其他工作中心问题

        List<WorkCenterPlan> list = this.list(Wrappers.<WorkCenterPlan>lambdaQuery()
                .eq(WorkCenterPlan::getWorkCenterCode, workCenterPlan.getWorkCenterCode())
                .ne(WorkCenterPlan::getWorkCenterPlanCode, workCenterPlanCode)
                .eq(WorkCenterPlan::getStatusCode, PlanStatusEnum.GOING.getType())
        );
        if (CollUtil.isNotEmpty(list)) {
            throw new BizException("工作中心正在执行其他工单，不能启动");
        }

        // TODO 校验工人信息


        // 其他校验


        // 修改工作中心数据
        workCenterPlan.setRealityStartDate(LocalDateTime.now());
        workCenterPlan.setStatus(PlanStatusEnum.GOING.getDescription());
        workCenterPlan.setStatusCode(PlanStatusEnum.GOING.getType());

        // TODO 计算工单的预计结束时间

        // 更新工作中心
        this.updateById(workCenterPlan);
        // TODO 抛出事件
    }


    @Override
    public WorkCenterPlan getByWorkCenterPlanCode(String workCenterPlanCode) {

        WorkCenterPlan one = this.getOne(Wrappers.<WorkCenterPlan>lambdaQuery()
                .eq(WorkCenterPlan::getWorkCenterPlanCode, workCenterPlanCode),false
        );


        return one;
    }


    @Override
    public WorkPlatformDTO infoWorkPlatform(String workCenterPlanCode) {

        WorkPlatformDTO workPlatformDTO = new WorkPlatformDTO();
        // 查询当前工单信息

        WorkCenterPlan workCenterPlan = this.getByWorkCenterPlanCode(workCenterPlanCode);

        // 工作中心数据复制
        BeanUtil.copyProperties(workCenterPlan, workPlatformDTO);
        workPlatformDTO.setWorkCenterName(workCenterPlan.getName());

        workPlatformDTO.setEmployee(AuthUtils.getUserInfo().getTruename());


        workPlatformDTO.setBanci(MesUtils.calcBanciByDate());

        // 订单数据复制


        // 查询其他未开始工单  查询 未开始， 暂停的工单，按照开始时间排序，取前10条
        List<WorkCenterPlan> workCenterPlanList = this.list(Wrappers.<WorkCenterPlan>lambdaQuery()
                .eq(WorkCenterPlan::getWorkCenterCode, workCenterPlan.getWorkCenterCode())
                .ne(WorkCenterPlan::getId, workCenterPlan.getId())
                .le(WorkCenterPlan::getStatusCode, PlanStatusEnum.PAUSED.getType())
                .orderByDesc(WorkCenterPlan::getPlanStartDate)
                .last("limit 10")
        );

        workPlatformDTO.setWorkCenterPlanList(workCenterPlanList);


        //  获取当前工单对应在本你工作中心可用物料的信息
        List<WorkCenterMatPlatformDTO> workCenterMatPlatFormDTOS = workCenterMatService
                .listGroupByWorkCenter(workCenterPlan.getPlanCode());
        workPlatformDTO.setWorkCenterMatPlatformDTOList(workCenterMatPlatFormDTOS);


        return workPlatformDTO;
    }

    /**
     * 报工
     * 逻辑
     * 校验工单状态是否正确
     * 校验报工数据是否正确
     * <p>
     * 校验当前计划是否因为外部原因不能生产
     * 设备质检
     * 产品质检 等
     * <p>
     * <p>
     * 倒冲物料 加锁
     * 校验报工倒冲物料是否充足
     * <p>
     * 是否需要打印数据
     * <p>
     * <p>
     * 本次报工完成该工单是否结束
     * <p>
     * <p>
     * <p>
     * // 根据不同的报工模式选择对应的装箱逻辑
     * // 非一物一码整箱报工   一物一码不装箱报工   一物一码装箱报工
     * // 根据工作中心的种类不同选择不同的物料倒冲模式
     *
     * @param workDTO
     */
    @Override
    @Transactional
    public void work(WorkDTO workDTO) {

        WorkCenterPlan workCenterPlan = getByWorkCenterPlanCode(workDTO.getWorkCenterPlanCode());

        if (!PlanStatusEnum.GOING.getType().equals(workCenterPlan.getStatusCode())) {
            throw new BizException("工单不是进行中不能报工");
        }

        if (!"output".equals(workCenterPlan.getMode())) {
            throw new BizException("该工作中心不能进行报工");
        }

        // 获取不同的逻辑进行处理 处理完成 反馈需要的信息
        WorkHandle workHandle = workHandleMap.get(workDTO.getType());
        WorkCenterPlan execute = workHandle.execute(workCenterPlan, workDTO);
        // 更新对应的计划
        updateById(execute);


        WorkReportLog build = WorkReportLog.builder()
                .planCode(workCenterPlan.getPlanCode())
                .workCenterCode(workCenterPlan.getWorkCenterCode())
                .num(workDTO.getNum())
                .matCode(workCenterPlan.getProductCode())
                .matName(workCenterPlan.getProductName())
                .boxCode(workDTO.getPackageCode())
                .build();


        workReportLogService.saveWorkReport(build);


        // 抛出报工完成后的事件
        applicationEventPublisher.publishEvent(new WorkCenterWorkEvent(workCenterPlan, workDTO.getNum()));

    }

    /**
     * 物料进行投料
     * 半成品投料原材料投料
     * <p>
     * 目前只考虑半成品投料
     * <p>
     * <p>
     * 原材了走领料直接使用
     *
     * @param devoteMaterail
     */
    @Override
    @Transactional
    public void devoteMaterail(DevoteMaterail devoteMaterail) {

        WorkCenterPlan workCenterPlan = this.getByWorkCenterPlanCode(devoteMaterail.getWorkCenterCode());
        if(!workCenterPlan.getStatusCode().equals(PlanStatusEnum.GOING.getType())){
            throw new BizException("工作中心状态不对不能投料");
        }



        // 投料限制   校验
        String matBarcode = devoteMaterail.getMatBarcode();

        MatDetail matDetail = MesUtils.MatBarcodeSplit(matBarcode);
        // 查询bom 看是否能投料   // 这里看对应的工作中心输入 物料，，如果是空，则查询bom  否则不能投料


        // 投料 取线边库物料
        mesSideWarehouseService.output(matDetail.getBarcode(), matDetail.getNum(), "",null);


        WorkCenterMat workCenterMat = workCenterMatService.queryByBarcodeAndSupplier(workCenterPlan.getWorkCenterCode(), matDetail.getBarcode(), matDetail.getSupplier(),workCenterPlan.getPlanCode());
        // 抽出公用的数据
        if (workCenterMat == null) {
            workCenterMat = new WorkCenterMat();
            workCenterMat.setWorkCenterCode(workCenterPlan.getWorkCenterCode());
            workCenterMat.setWorkCenterName(workCenterPlan.getName());

            workCenterMat.setMatCode(matDetail.getMatCode());
            workCenterMat.setMatBarcode(matDetail.getBarcode());
            workCenterMat.setMatName(matDetail.getMatName());
            workCenterMat.setNum(matDetail.getNum());
            workCenterMat.setCurrentNumber(matDetail.getNum());
            workCenterMat.setSupplierCode(matDetail.getSupplier());
            workCenterMat.setProductCode(workCenterPlan.getProductCode());
            workCenterMat.setProductName(workCenterPlan.getProductName());
            workCenterMat.setPlanCode(workCenterPlan.getPlanCode());
        } else {
            workCenterMat.setCurrentNumber(workCenterMat.getCurrentNumber() + matDetail.getNum());
            workCenterMat.setNum(workCenterMat.getNum() + matDetail.getNum());
        }
        workCenterMatService.save(workCenterMat);

        //  投料物料批次发生变化  记录变化点




        // 投料记录
        DevoteMaterailLog build = DevoteMaterailLog.builder()
                .matCode(matDetail.getMatCode())
                .matName(matDetail.getMatName())
                .matBarcode(matDetail.getOldBarcode())
                .planCode(workCenterPlan.getPlanCode())
                .workCenterCode(workCenterPlan.getWorkCenterCode())
                .num(matDetail.getNum())
                .build();
        devoteMaterailLogService.saveDevoteLog(build);

    }


    /**
     * 暂停工作中心任务
     *
     * @param workCenterPlanCode
     */
    @Override
    public void suspend(String workCenterPlanCode) {
        WorkCenterPlan workCenterPlan = this.getByWorkCenterPlanCode(workCenterPlanCode);

        if (!workCenterPlan.getStatusCode().equals(PlanStatusEnum.GOING.getType())) {
            throw new BizException("工作中心不在进行中不能暂停");
        }

        update(Wrappers.<WorkCenterPlan>lambdaUpdate()
                .eq(WorkCenterPlan::getWorkCenterPlanCode, workCenterPlanCode)
                .set(WorkCenterPlan::getStatusCode, PlanStatusEnum.PAUSED.getType())
                .set(WorkCenterPlan::getStatus, PlanStatusEnum.PAUSED.getDescription())
        );

    }

    /**
     * 工作中心计划停止
     * @param workCenterPlanCode
     */
    @Override
    public void close(String workCenterPlanCode) {

        WorkCenterPlan workCenterPlan = this.getByWorkCenterPlanCode(workCenterPlanCode);
        if (!workCenterPlan.getStatusCode().equals(PlanStatusEnum.GOING.getType())) {
            throw new BizException("工作中心不在进行中不能暂停");
        }

        update(Wrappers.<WorkCenterPlan>lambdaUpdate()
                .eq(WorkCenterPlan::getWorkCenterPlanCode, workCenterPlanCode)
                .set(WorkCenterPlan::getStatusCode, PlanStatusEnum.CLOSE.getType())
                .set(WorkCenterPlan::getStatus, PlanStatusEnum.CLOSE.getDescription())
        );

    }
}
